package yartiss.engine.simulation.system.schedulable;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import yartiss.engine.simulation.IEnergyConsumptionProfile;
import yartiss.engine.simulation.io.ITaskElementVisitor;
import yartiss.engine.simulation.system.ITask;
import yartiss.engine.simulation.system.PeriodicTask;

/**
 * Subtasks of the graph task
 * 
 * @author Manar Qamhieh
 * 
 */
public class SubTask extends PeriodicTask {

	private ArrayList<SubTask> parentTasks;
	private ArrayList<SubTask> childrenTasks;
	private long earliestFinishTime;
	private long latestFinisheTime;
	private int nbProc;
	private List<Integer> indices;
	private long graphLaxity;

	public SubTask(long firstRelease, long wcet, long wcee, long period,
			long deadline, int priority, int nbProc, IEnergyConsumptionProfile p) {
		super(firstRelease, wcet, wcee, period, deadline, priority, p);

		this.nbProc = nbProc;
		parentTasks = new ArrayList<SubTask>();
		childrenTasks = new ArrayList<SubTask>();
		indices = new ArrayList<Integer>();

	}

	public SubTask(SubTask subtask) {
		this(subtask.getFirstRelease(), subtask.getWcet(), subtask.getWcee(),
				subtask.getPeriod(), subtask.getDeadline(), subtask
						.getPriority(), subtask.getNumOfProc(), subtask
						.getEnergyConsumptionProfile());
	}

	/**
	 * Sets the index of a subtask in the graph according to the index of the
	 * last subtask in the list
	 * 
	 * @param list
	 *            list of all subtasks
	 */

	public void setIndex(ArrayList<SubTask> list) {
		if (list.isEmpty())
			setId(0);
		else
			setId(list.get(list.size() - 1).getId() + 1);
	}

	public void setIndex(CopyOnWriteArrayList<SubTask> list) {
		setId(list.get(list.size() - 1).getId() + 1);
	}

	/**
	 * gets the parent subtasks of the subtask
	 * 
	 * @return parent subtasks
	 */
	public ArrayList<SubTask> getParentTasks() {
		return parentTasks;
	}

	/**
	 * sets the parent subtasks of the subtask
	 * 
	 * @param parents
	 *            parent subtasks
	 */
	public void setParentTasks(ArrayList<SubTask> parents) {
		this.parentTasks = parents;
	}

	/**
	 * gets the children subtasks of the subtask
	 * 
	 * @return children subtasks
	 */
	public ArrayList<SubTask> getChildrenTasks() {
		return childrenTasks;
	}

	/**
	 * sets the children subtasks of the subtask
	 * 
	 * @param children
	 *            children subtasks
	 */
	public void setChildrenTasks(ArrayList<SubTask> children) {
		this.childrenTasks = children;
	}

	/**
	 * adds a child to the subtask
	 * 
	 * @param child
	 *            child subtask
	 */
	public void addChildTask(SubTask child) {
		childrenTasks.add(child);
		child.getParentTasks().add(this);
	}

	/**
	 * adds a parent to the subtask
	 * 
	 * @param parent
	 *            parent subtask
	 */
	public void addParentTask(SubTask parent) {
		parentTasks.add(parent);
		parent.getChildrenTasks().add(this);
	}

	/**
	 * gets the earliest time of a subtask to finish its execution
	 * 
	 * @return earliest execution time
	 */
	public long getEarliestFinishTime() {
		return earliestFinishTime;
	}

	/**
	 * sets the earliest time of a subtask to finish its execution
	 * 
	 * @param time
	 *            earliest execution time
	 */
	public void setEarliestFinishTime(long time) {
		this.earliestFinishTime = time;
	}

	/**
	 * gets the latest time of a subtask to finish its execution
	 * 
	 * @return latest execution time
	 */
	public long getLatestFinisheTime() {
		return latestFinisheTime;
	}

	/**
	 * sets the latest time of a subtask to finish its execution
	 * 
	 * @param time
	 *            latest execution time
	 */
	public void setLatestFinisheTime(long time) {
		this.latestFinisheTime = time;
	}

	/**
	 * gets the number of processors on which a subtask can execute
	 * 
	 * @return number of processors
	 */
	public int getNumOfProc() {
		return nbProc;
	}

	/**
	 * sets the number of processors on which a subtask can execute
	 * 
	 * @param numOfProc
	 *            number of processors
	 */
	public void setNumOfProc(int nbProc) {
		this.nbProc = nbProc;
	}

	/**
	 * Verifies if the subtask is one of critical path subtasks
	 * 
	 * @return true if it is critical subtask, false otherwise
	 */
	public boolean isCriticalTask() {
		if (earliestFinishTime == latestFinisheTime)
			return true;
		else
			return false;
	}

	/**
	 * Checks if the subtask is the starting subtask of the graph, which is the
	 * only subtask in the graph which has no parents
	 * 
	 * @return true if it is the starting of a graph, false otherwise
	 */
	public boolean isStartingSubtask() {
		if (parentTasks.isEmpty())
			return true;
		else
			return false;
	}

	/**
	 * Checks if the subtask is the ending subtask of the graph, which is the
	 * only subtask in the graph which has no children
	 * 
	 * @return true if it is the ending of a graph, false otherwise
	 */
	public boolean isEndingSubtask() {
		if (childrenTasks.isEmpty())
			return true;
		else
			return false;
	}

	public String toString() {
		return "[" + getId() + "] wcet:" + getWcet() + " \tdeadline: "
				+ getDeadline() + " \tnbProc:" + getNumOfProc()
				+ "\tfirstRelease: " + getFirstRelease();
	}

	public void accept(ITaskElementVisitor visitor) {
		visitor.visitSubTask(this);
	}

	@Override
	public ITask cloneTask() {
		return new SubTask(this);
	}

	// public long setFirstRelease() {
	// long offset = earliestFinishTime - getWcet();
	// if (offset < 0)
	// return 0;
	// else
	// return offset;
	// }

	public void setChildrenIndices(String[] indices) {
		if (indices == null)
			this.indices = null;
		for (String childId : indices) {
			this.indices.add(Integer.parseInt(childId));
		}
	}

	public List<Integer> getChildrenIndices() {
		return indices;
	}

	@Override
	public String getType() {
		return "subtask";
	}

	/**
	 * Laxity of a subtask is the difference between the finish time of this
	 * subtask after calculating the critical path
	 */
	@Override
	public long getLaxity() {
		if (latestFinisheTime == 0 || earliestFinishTime == 0)
			throw new IllegalArgumentException(
					"Critical path should  be calculated before using finish time");
		return latestFinisheTime - earliestFinishTime;
	}

	/**
	 * @return the laxity of the graph to which this subtask belongs
	 */
	public long getGraphLaxity() {
		return graphLaxity;
	}

	/**
	 * @param graphLaxity
	 *            the graph's laxity to set
	 */
	public void setGraphLaxity(long graphLaxity) {
		this.graphLaxity = graphLaxity;
	}

}